#include <limits.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <qpe/qpeapplication.h>
#include <qpe/qcopenvelope_qws.h>
#include <qlayout.h>
#include <qlineedit.h>
#include <qtabbar.h>
#include <qwidgetstack.h>
#include <qevent.h>

#include "winuser.h"
#include "WinRemoconProtocol.h"
#include "ZRemocon.h"
#include "MouseWidget.h"
#include "MouseDragWidget.h"
#include "RemoconWidget.h"
#include "ConfigWidget.h"
#include "KeyBindWidget.h"
#include "keycodeutil.h"

ZRemocon::ZRemocon(QWidget *parent): QWidget(parent, NULL),
		config(APP_NAME),
		closeAccepted(false),
		/*_screenOrientation(None)*/
		keyBindManager(&config)
{
	// widgets
	mouseWidget = new MouseWidget(&config, this);
	connect(mouseWidget, SIGNAL(mouseMovedAbsolute(unsigned short, unsigned short)), this, SLOT(sendMouseMoveAbsolute(unsigned short, unsigned short)));
	connect(mouseWidget, SIGNAL(mouseMovedRelative(signed char, signed char)), this, SLOT(sendMouseMoveRelative(signed char, signed char)));
	connect(mouseWidget, SIGNAL(mouseLeftButtonClicked()), this, SLOT(sendMouseLeftButtonClick()));
	connect(mouseWidget, SIGNAL(mouseRightButtonClicked()), this, SLOT(sendMouseRightButtonClick()));
	
	mouseDragWidget = new MouseDragWidget(this);
	connect(mouseDragWidget, SIGNAL(mouseMovedAbsolute(unsigned short, unsigned short)), this, SLOT(sendMouseMoveAbsolute(unsigned short, unsigned short)));
	connect(mouseDragWidget, SIGNAL(mouseMovedRelative(signed char, signed char)), this, SLOT(sendMouseMoveRelative(signed char, signed char)));
	connect(mouseDragWidget, SIGNAL(mouseLeftButtonPressed()), this, SLOT(sendMouseLeftButtonPress()));
	connect(mouseDragWidget, SIGNAL(mouseLeftButtonReleased()), this, SLOT(sendMouseLeftButtonRelease()));
	connect(mouseDragWidget, SIGNAL(mouseRightButtonPressed()), this, SLOT(sendMouseRightButtonPress()));
	connect(mouseDragWidget, SIGNAL(mouseRightButtonReleased()), this, SLOT(sendMouseRightButtonRelease()));
	
	remoconWidget = new RemoconWidget(this);
	connect(remoconWidget, SIGNAL(keyPressed(unsigned char)), this, SLOT(sendKeyPress(unsigned char)));
	connect(remoconWidget, SIGNAL(keyReleased(unsigned char)), this, SLOT(sendKeyRelease(unsigned char)));
	
	configWidget = new ConfigWidget(&config, this);
	connect(configWidget, SIGNAL(quitButtonClicked()), this, SLOT(closeApplication()));
	connect(configWidget, SIGNAL(rotateScreenButtonClicked()), this, SLOT(rotateScreen()));
	connect(configWidget, SIGNAL(turnOffDisplayButtonClicked()), this, SLOT(turnOffDisplay()));
	connect(configWidget, SIGNAL(enableKeyHelperButtonClicked()), this, SLOT(enableKeyHelper()));
	connect(configWidget, SIGNAL(disableKeyHelperButtonClicked()), this, SLOT(disableKeyHelper()));
	
	// keyBindManager.load();
	keyBindWidget = new KeyBindWidget(&keyBindManager, this);
	
	view = new QWidgetStack(this);
	view->addWidget(mouseWidget, Widget_MouseWidget);
	view->addWidget(mouseDragWidget, Widget_MouseDragWidget);
	view->addWidget(remoconWidget, Widget_RemoconWidget);
	view->addWidget(configWidget, Widget_ConfigWidget);
	view->addWidget(keyBindWidget, Widget_KeyBindWidget);
	view->raiseWidget(mouseWidget);
	
	tabBar = new QTabBar(this);
	tabBar->setFocusPolicy(QWidget::NoFocus);
	tabBar->setShape(QTabBar::TriangularAbove);
	tabBar->insertTab(new QTab("Mouse"), Widget_MouseWidget);
	tabBar->insertTab(new QTab("Drag"), Widget_MouseDragWidget);
	tabBar->insertTab(new QTab("Remocon"), Widget_RemoconWidget);
	tabBar->insertTab(new QTab("Config"), Widget_ConfigWidget);
	tabBar->insertTab(new QTab("Bind"), Widget_KeyBindWidget);
	connect(tabBar, SIGNAL(selected(int)), view, SLOT(raiseWidget(int)));
	connect(tabBar, SIGNAL(selected(int)), this, SLOT(updateView(int)));
	
	// layout
	QVBoxLayout *l = new QVBoxLayout(this);
	l->addWidget(tabBar);
	l->addWidget(view);
	
	// show default tab
	tabBar->setCurrentTab(prevWidgetIndex = Widget_MouseWidget);
	
	// socket
	_socket = socket(AF_INET, SOCK_DGRAM, 0);
	_address.sin_family = AF_INET;
	_address.sin_port = htons(configWidget->port());
	_address.sin_addr.s_addr = inet_addr(configWidget->hostIPAddress().latin1());
	
/*
	// maximize
	showFullScreen();
	
*/
	// grab keyboard
	QPEApplication::grabKeyboard();
	grabKeyboard();
	
	if (configWidget->isDisableKeyHelperChecked())
		disableKeyHelper();
}

void ZRemocon::closeEvent(QCloseEvent *e)
{
	enableKeyHelper();
	releaseKeyboard();
	QPEApplication::ungrabKeyboard();
	
	if (closeAccepted)
	{
		configWidget->save();
		close(_socket);
		e->accept();
	}
	else
	{
		e->ignore();
	}
}

void ZRemocon::closeApplication()
{
	closeAccepted = true;
	close();
}

void ZRemocon::rotateScreen()
{
	QCopEnvelope e("QPE/System", "excute(QString)");
	e << "__rotate";
}

void ZRemocon::turnOffDisplay()
{
	QCopEnvelope("QPE/System", "setBlankLCD(int)") << 1;
}

void ZRemocon::enableKeyHelper()
{
	QCopEnvelope("QPE/KeyHelper", "enable()");
}

void ZRemocon::disableKeyHelper()
{
	QCopEnvelope("QPE/KeyHelper", "disable()");
}

void ZRemocon::resizeEvent(QResizeEvent *)
{
	if (size() == QApplication::desktop()->size())
		return;
	
/*
	if (QApplication::desktop()->size().width() == 640 && _screenOrientation == ViewStyle)
	{
		_screenOrientation = InputStyle;
	}
	else if (QApplication::desktop()->size().width() == 480 && _screenOrientation == InputStyle)
	{
		_screenOrientation = ViewStyle;
	}
*/
	
	setFixedSize(QApplication::desktop()->size());
	reparent(NULL, WStyle_StaysOnTop | WStyle_Customize | WStyle_NoBorderEx, QPoint(0, 0));
	showNormal();
	showFullScreen();
}

void ZRemocon::keyPressEvent(QKeyEvent *e)
{
	/*
		@todo:
		Fn + Calendar: Mouse
		Fn + Address: Drag
		Fn + Mail: Remocon
	*/
	const KeyBindManager::Key &key = keyBindManager.hostKeyCodeByQtKeyCode(e->key());
	if (key.code == 0x00)	return;
	
	if (key.shift)	sendKeyPress(VK_SHIFT);
	sendKeyPress(key.code);
	e->accept();
}

void ZRemocon::keyReleaseEvent(QKeyEvent *e)
{
	const KeyBindManager::Key &key = keyBindManager.hostKeyCodeByQtKeyCode(e->key());
	if (key.code == 0x00)	return;
	
	sendKeyRelease(key.code);
	if (key.shift)	sendKeyRelease(VK_SHIFT);
	e->accept();
}

void ZRemocon::sendKeyPress(unsigned char key)
{
	// qDebug("press: %s", HostKeyNameByCode(key).ascii());
	
	unsigned char data[2];
	data[0] = KEY_PRESS;
	data[1] = key;
	
	sendto(_socket, data, 2, 0, (struct sockaddr *)&_address, sizeof(_address));
}

void ZRemocon::sendKeyRelease(unsigned char key)
{
	// qDebug("release: %s", HostKeyNameByCode(key).ascii());
	
	unsigned char data[2];
	data[0] = KEY_RELEASE;
	data[1] = key;
	
	sendto(_socket, data, 2, 0, (struct sockaddr *)&_address, sizeof(_address));
}

void ZRemocon::sendMouseMoveAbsolute(unsigned short x, unsigned short y)
{
	if (!configWidget->mouseMoveAbsolute())
		return;
	
	unsigned char data[5];
	unsigned short *dp = (unsigned short *)&data[1];
	*dp++ = htons(x);
	*dp = htons(y);
	data[0] = MOUSE_MOVE_ABSOLUTE_WORD;
	
	sendto(_socket, data, 5, 0, (struct sockaddr *)&_address, sizeof(_address));
}

void ZRemocon::sendMouseMoveRelative(signed char x, signed char y)
{
	if (configWidget->mouseMoveAbsolute())
		return;
	
	unsigned char data[3];
	data[0] = MOUSE_MOVE_RELATIVE;
	data[1] = static_cast<char>(x * (configWidget->cursorAccel() / 100.0));
	data[2] = static_cast<char>(y * (configWidget->cursorAccel() / 100.0));
	
	sendto(_socket, data, 3, 0, (struct sockaddr *)&_address, sizeof(_address));
}

void ZRemocon::sendMouseLeftButtonClick()
{
	unsigned char data[2];
	data[0] = MOUSE_BUTTON_CLICK;
	data[1] = MOUSE_LEFT_BUTTON;
	
	sendto(_socket, data, 2, 0, (struct sockaddr *)&_address, sizeof(_address));
}

void ZRemocon::sendMouseLeftButtonPress()
{
	unsigned char data[2];
	data[0] = MOUSE_BUTTON_PRESS;
	data[1] = MOUSE_LEFT_BUTTON;
	
	sendto(_socket, data, 2, 0, (struct sockaddr *)&_address, sizeof(_address));
}

void ZRemocon::sendMouseLeftButtonRelease()
{
	unsigned char data[2];
	data[0] = MOUSE_BUTTON_RELEASE;
	data[1] = MOUSE_LEFT_BUTTON;
	
	sendto(_socket, data, 2, 0, (struct sockaddr *)&_address, sizeof(_address));
}

void ZRemocon::sendMouseRightButtonClick()
{
	unsigned char data[2];
	data[0] = MOUSE_BUTTON_CLICK;
	data[1] = MOUSE_RIGHT_BUTTON;
	
	sendto(_socket, data, 2, 0, (struct sockaddr *)&_address, sizeof(_address));
}

void ZRemocon::sendMouseRightButtonPress()
{
	unsigned char data[2];
	data[0] = MOUSE_BUTTON_PRESS;
	data[1] = MOUSE_RIGHT_BUTTON;
	
	sendto(_socket, data, 2, 0, (struct sockaddr *)&_address, sizeof(_address));
}

void ZRemocon::sendMouseRightButtonRelease()
{
	unsigned char data[2];
	data[0] = MOUSE_BUTTON_RELEASE;
	data[1] = MOUSE_RIGHT_BUTTON;
	
	sendto(_socket, data, 2, 0, (struct sockaddr *)&_address, sizeof(_address));
}

void ZRemocon::updateView(int index)
{
	switch (prevWidgetIndex)
	{
		case Widget_KeyBindWidget:
			keyBindWidget->updateKeyBindManager();
			break;
		case Widget_ConfigWidget:
			configWidget->save();
			resetSocket();
			break;
	}
	
	switch (index)
	{
		case Widget_MouseWidget:
		case Widget_MouseDragWidget:
		case Widget_RemoconWidget:
			switch (prevWidgetIndex)
			{
				case Widget_ConfigWidget:
				case Widget_KeyBindWidget:
					grabKeyboard();
					break;
			}
			break;
		
		case Widget_ConfigWidget:
		case Widget_KeyBindWidget:
			switch (prevWidgetIndex)
			{
				case Widget_MouseWidget:
				case Widget_MouseDragWidget:
				case Widget_RemoconWidget:
					// to edit IP address
					releaseKeyboard();
					break;
			}
			break;
	}
	
	prevWidgetIndex = index;
}

void ZRemocon::resetSocket()
{
	_address.sin_port = htons(configWidget->port());
	_address.sin_addr.s_addr = inet_addr(configWidget->hostIPAddress().latin1());
}